﻿using AspectCore.DynamicProxy;
using AspectCore.DynamicProxy.Parameters;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Service.Core.Redis.Services;
using Service.Framework;

namespace Service.Core.Redis.Aop.Cache
{
    public class RedisCacheInterceptorAttribute : DefaultInterceptor
    {


        private readonly string? _cacheKey;

        /// <summary>
        /// 缓存时长 (秒) 默认存储 10s 如果0代表永久
        /// </summary>
        /// <value></value>
        private readonly long _cacheDuration;


        /// <summary>
        /// /
        /// </summary>
        /// <param name="cacheKey">为空，默认给代理名称</param>
        /// <param name="cacheDuration"></param>
        public RedisCacheInterceptorAttribute(string cacheKey, long cacheDuration = 10)
        {
            _cacheKey = cacheKey ?? throw new ArgumentNullException(nameof(cacheKey));
            _cacheDuration = cacheDuration;
        }

        public override int Order => 2;

        public override async Task Invoke(AspectContext context, AspectDelegate next)
        {
            var redisService = context.ServiceProvider.GetService<IRedisService>();
            if (redisService == null)
                throw new ArgumentNullException(nameof(redisService));
            var cacheKey =  GetDefaultKey(context,_cacheKey);
            var methodReturnType = context.GetReturnParameter().Type;
            var returnType = methodReturnType;
            //判断key是否存在
            if (await redisService.Database.KeyExistsAsync(cacheKey))
            {
                if (context.IsAsync())
                {
                    returnType = returnType.GenericTypeArguments.FirstOrDefault();
                }
                var cacheResult = await redisService.Database.StringGetAsync(cacheKey);
                var value = JsonConvert.DeserializeObject(cacheResult.ToString(), returnType);
                if (methodReturnType == typeof(Task<>).MakeGenericType(returnType))
                {
                    //反射获取Task<>类型的返回值，相当于Task.FromResult(value)
                    context.ReturnValue = typeof(Task).GetMethod(nameof(Task.FromResult)).MakeGenericMethod(returnType).Invoke(null, new[] { value });
                }
                else if (methodReturnType == typeof(ValueTask<>).MakeGenericType(returnType))
                {
                    context.ReturnValue = Activator.CreateInstance(typeof(ValueTask<>).MakeGenericType(returnType), value);
                }
                context.ReturnValue = typeof(Task).GetMethod(nameof(Task.FromResult))
                    .MakeGenericMethod(returnType).Invoke(null, new[] { value });
            }
            else
            {
                await next(context);
                object value = "";
                if (context.IsAsync())
                {
                    value = await context.UnwrapAsyncReturnValue();
                }
                else
                    value = context.ReturnValue;
                TimeSpan? timeout = null;
                if (_cacheDuration != 0)
                {
                    timeout = TimeSpan.FromSeconds(_cacheDuration);
                }
                await redisService.Database.StringSetAsync(cacheKey, JsonConvert.SerializeObject(value), timeout);
            }
        }
    }
}
